<?php

declare(strict_types=1);

use Hyperf\Utils\ApplicationContext;
use Hyperf\Contract\StdoutLoggerInterface;
use Hyperf\Contract\SessionInterface;
use Hyperf\Logger\LoggerFactory;
use Hyperf\HttpServer\Contract\RequestInterface;
use Hyperf\HttpServer\Contract\ResponseInterface;
use Hyperf\HttpServer\Router\Dispatched;
use Hyperf\ExceptionHandler\Formatter\FormatterInterface;
use Psr\SimpleCache\CacheInterface;
use Psr\EventDispatcher\EventDispatcherInterface;
use Psr\Log\LoggerInterface;

if (! function_exists('container')) {
    /**
     * 容器实例.
     *
     * @return null|\Psr\Container\ContainerInterface
     */
    function container()
    {
        return ApplicationContext::getContainer();
    }
}

if (! function_exists('response')) {
    /**
     * response 实例
     *
     * @return ResponseInterface|mixed
     * @throws \Psr\Container\ContainerExceptionInterface
     * @throws \Psr\Container\NotFoundExceptionInterface
     */
    function response(): mixed
    {
        return container()->get(ResponseInterface::class);
    }
}

if (! function_exists('di')) {
    /**
     * @param string $id
     * @return mixed
     * @throws \Psr\Container\ContainerExceptionInterface
     * @throws \Psr\Container\NotFoundExceptionInterface
     */
    function di(string $id = ''): mixed
    {
        return container($id);
    }
}

if (! function_exists('stdLog')) {
    /**
     * 控制台日志
     *
     * @return mixed
     * @throws \Psr\Container\ContainerExceptionInterface
     * @throws \Psr\Container\NotFoundExceptionInterface
     */
    function stdLog(): mixed
    {
        return container()->get(StdoutLoggerInterface::class);
    }
}

if (! function_exists('logger')) {
    /**
     * 文件日志
     *
     * @param string $name
     * @param string $group
     * @return LoggerInterface
     * @throws \Psr\Container\ContainerExceptionInterface
     * @throws \Psr\Container\NotFoundExceptionInterface
     */
    function logger(string $name = 'hyperf', string $group = 'default'): LoggerInterface
    {
        return container()->get(LoggerFactory::class)->get($name, $group);
    }
}

if (! function_exists('event_dispatch')) {
    /**
     * 事件分发
     *
     * @param object $event  事件对象
     * @return object
     * @throws \Psr\Container\ContainerExceptionInterface
     * @throws \Psr\Container\NotFoundExceptionInterface
     */
    function event_dispatch(object $event): object
    {
        return container()->get(EventDispatcherInterface::class)->dispatch($event);
    }
}

if (!function_exists('format_throwable')) {
    /**
     * Format a throwable to string.
     */
    function format_throwable(\Throwable $throwable): string
    {
        return di()->get(FormatterInterface::class)->format($throwable);
    }
}

if (!function_exists('session')) {
    /**
     * session 对象
     *
     * @return SessionInterface|mixed
     */
    function session()
    {
        return container()->get(SessionInterface::class);
    }
}

if (!function_exists('cache')) {
    /**
     * 缓存实例 简单的缓存.
     *
     * @return mixed|\Psr\SimpleCache\CacheInterface
     */
    function cache()
    {
        return container()->get(CacheInterface::class);
    }
}

if (!function_exists('is_iterable')) {
    function is_iterable($var): bool
    {
        return (is_array($var) || $var instanceof Traversable || $var instanceof stdClass);
    }
}


if (! function_exists('get_current_action')) {
    /**
     * 获取当前请求的控制器和方法
     *
     * @return array
     * @throws \Psr\Container\ContainerExceptionInterface
     * @throws \Psr\Container\NotFoundExceptionInterface
     */
    function get_current_action(): array
    {
        $obj = container()->get(RequestInterface::class)->getAttribute(Dispatched::class);

        if (property_exists($obj, 'handler')
            && isset($obj->handler)
            && property_exists($obj->handler, 'callback')
        ) {
            $action = $obj->handler->callback;
        } else {
            throw new \Exception('The route is undefined! Please check!');
        }

        $errMsg = 'The controller and method are not found! Please check!';
        if (is_array($action)) {
            list($controller, $method) = $action;
        } elseif (is_string($action)) {
            if (strstr($action, '::')) {
                list($controller, $method) = explode('::', $action);
            } elseif (strstr($action, '@')) {
                list($controller, $method) = explode('@', $action);
            } else {
                list($controller, $method) = [false, false];
                stdLog()->error($errMsg);
            }
        } else {
            list($controller, $method) = [false, false];
            stdLog()->error($errMsg);
        }

        return compact('controller', 'method');
    }
}

if (! function_exists('request')) {
    /**
     * request 实例
     *
     * @return RequestInterface|mixed
     * @throws \Psr\Container\ContainerExceptionInterface
     * @throws \Psr\Container\NotFoundExceptionInterface
     */
    function request(): mixed
    {
        return container()->get(RequestInterface::class);
    }
}

if (! function_exists('get_client_ip')) {
    /**
     * 获取客户端 ip
     *
     * @return mixed|string
     * @throws \Psr\Container\ContainerExceptionInterface
     * @throws \Psr\Container\NotFoundExceptionInterface
     */
    function get_client_ip(): string
    {
        $request = request();
        return $request->getHeaderLine('X-Forwarded-For')
            ?: $request->getHeaderLine('X-Real-IP')
            ?: ($request->getServerParams()['remote_addr'] ?? '')
            ?: '127.0.0.1';
    }
}

if (! function_exists('route_original')) {
    /**
     * 获取路由地址
     *
     * @param bool $withParams 是否需要携带参数
     * @return string
     * @throws \Psr\Container\ContainerExceptionInterface
     * @throws \Psr\Container\NotFoundExceptionInterface
     */
    function route_original(bool $withParams = false): string
    {
        $obj = request()->getAttribute(Dispatched::class);

        if (! property_exists($obj, 'handler')
            || ! isset($obj->handler)
            || ! property_exists($obj->handler, 'route')
        ) {
            throw new \Exception('The route is undefined! Please check!');
        }

        if ($withParams) {
            // eg: "/foo/bar/article/detail/123"
            return request()->getPathInfo();
        }

        // eg: "/foo/bar/{hello}/detail/{id:\d+}"
        return $obj->handler->route;
    }
}
